home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr49 / 129_01.zip / 210MSG.C < prev    next >
Text File  |  1993-06-01  |  33KB  |  1,104 lines

  1. /************************************************************************/
  2. /*                msg.c                    */
  3. /*                                    */
  4. /*    Message handling for Citadel bulletin board system        */
  5. /************************************************************************/
  6.  
  7. /************************************************************************/
  8. /*                history                 */
  9. /*                                    */
  10. /* 84Dec16 HAW&JLS  printMessage modified for new WC protocol.        */
  11. /* 84Dec08 HAW    Fix bug caused by HAW when he implemented automailresp. */
  12. /* 84Sep18 JLS    Normalize recipient's name.                */
  13. /* 84Jul03 HAW    mPrintf, dPrintf, and mWCprintf upgraded to BDS 1.50a.    */
  14. /* 84Jun23 HAW & JLS  Unused local variables zapped.            */
  15. /* 84Jun07 HAW    Responses in Mail now automatically get recipient.    */
  16. /* 84May07 JLS & HAW Implemented the configuration switch ENTEROK.    */
  17. /* 84Mar29 HAW Start upgrade to BDS C 1.50a, identify _spr problem.    */
  18. /* 83Mar03 CrT & SB   Various bug fixes...                */
  19. /* 83Feb27 CrT    Save private mail for sender as well as recipient.    */
  20. /* 83Feb23    Various.  transmitFile() won't drop first char on WC... */
  21. /* 82Dec06 CrT    2.00 release.                        */
  22. /* 82Nov05 CrT    Stream retrieval.  Handles messages longer than MAXTEXT.*/
  23. /* 82Nov04 CrT    Revised disk format implemented.            */
  24. /* 82Nov03 CrT    Individual history begun.  General cleanup.        */
  25. /************************************************************************/
  26.  
  27. #include <210ctdl.h>
  28.  
  29. /************************************************************************/
  30. /*                contents                */
  31. /*                                    */
  32. /*    aideMessage()        saves auto message in Aide>        */
  33. /*    dGetWord()        reads a word off disk            */
  34. /*    dPrintf()        printf() that writes to disk        */
  35. /*    fakeFullCase()        converts uppercase message to mixed case*/
  36. /*    flushMsgBuf()        wraps up message-to-disk store        */
  37. /*    getMessage()        load message into RAM            */
  38. /*    getMsgChar()        returns successive chars off disk    */
  39. /*    getMsgStr()        reads a string out of message.buf    */
  40. /*    getWord()        gets one word from message buffer    */
  41. /*    mAbort()        checks for user abort of typeout    */
  42. /*    makeMessage()        menu-level message-entry routine    */
  43. /*    mFormat()        formats a string to modem and console    */
  44. /*    mPeek()         sysop debugging tool--shows ctdlmsg.sys */
  45. /*    mPrintf()        writes a line to modem & console    */
  46. /*    mWCprintf()        special mprintf for WC transfers    */
  47. /*    msgInit()        sets up catChar, catSect etc.        */
  48. /*    noteLogMessage()    enter message into log record        */
  49. /*    noteMessage()        enter message into current room     */
  50. /*    note2Message()        noteMessage() local            */
  51. /*    printMessage()        prints a message on modem & console    */
  52. /*    pullIt()        sysop special message-removal routine    */
  53. /*    putMessage()        write message to disk            */
  54. /*    putMsgChar()        writes successive message chars to disk */
  55. /*    putWord()        writes one word to modem & console    */
  56. /*    showMessages()        menu-level show-roomful-of-messages fn    */
  57. /*    startAt()        setup to read a message off disk    */
  58. /*    unGetMsgChar()        return a char to getMsgChar()        */
  59. /*    zapMsgFile()        initialize ctdlmsg.sys            */
  60. /************************************************************************/
  61.  
  62. /************************************************************************/
  63. /*    aideMessage() saves auto message in Aide>            */
  64. /************************************************************************/
  65. aideMessage(noteDeletedMessage)
  66. char noteDeletedMessage;
  67. {
  68.     int ourRoom;
  69.  
  70.     /* message is already set up in msgBuf.mbtext */
  71.     putRoom(ourRoom=thisRoom);
  72.     getRoom(AIDEROOM);
  73.  
  74.     strCpy(msgBuf.mbauth, "Citadel");
  75.     msgBuf.mbto[0] = '\0';
  76.     if (putMessage( /* uploading== */ FALSE))    noteMessage(0, ERROR);
  77.  
  78.     if (noteDeletedMessage)   {
  79.     note2Message(pulledMId, pulledMLoc);
  80.     }
  81.  
  82.     putRoom(AIDEROOM);
  83.     noteRoom();
  84.     getRoom(ourRoom);
  85. }
  86.  
  87. /************************************************************************/
  88. /*    dGetWord() fetches one word from current message, off disk    */
  89. /*    returns TRUE if more words follow, else FALSE            */
  90. /************************************************************************/
  91. char dGetWord(dest, lim)
  92. char *dest;
  93. int  lim;
  94. {
  95.     char getMsgChar();
  96.     char c;
  97.  
  98.     --lim;    /* play it safe */
  99.  
  100.     /* pick up any leading blanks: */
  101.     for (c = getMsgChar();   c == ' '  &&  c && lim;   c = getMsgChar()) {
  102.     if (lim) { *dest++ = c;   lim--; }
  103.     }
  104.  
  105.     /* step through word: */
  106.     for (         ;  c != ' ' && c && lim;   c = getMsgChar()) {
  107.     if (lim) { *dest++ = c;   lim--; }
  108.     }
  109.  
  110.     /* trailing blanks: */
  111.     for (         ;   c == ' ' && c && lim;   c = getMsgChar()) {
  112.     if (lim) { *dest++ = c;   lim--; }
  113.     }
  114.  
  115.     if (c)  unGetMsgChar(c);    /* took one too many    */
  116.  
  117.     *dest = '\0';        /* tie off string    */
  118.  
  119.     return  c;
  120. }
  121.  
  122. /************************************************************************/
  123. /*    dPrintf() write from format+args to disk            */
  124. /************************************************************************/
  125. dPrintf(format /* plus an unknown #arguments for format */)
  126. char *format;
  127. {
  128.     int  putMsgChar();
  129.  
  130.     _spr(&format, putMsgChar);
  131. }
  132.  
  133. /************************************************************************/
  134. /*    fakeFullCase() converts a message in uppercase-only to a    */
  135. /*    reasonable mix.  It can't possibly make matters worse...    */
  136. /*    Algorithm: First alphabetic after a period is uppercase, all    */
  137. /*    others are lowercase, excepting pronoun "I" is a special case.    */
  138. /*    We assume an imaginary period preceding the text.        */
  139. /************************************************************************/
  140. fakeFullCase(text)
  141. char *text;
  142. {
  143.     char toLower(), toUpper();
  144.     char *c;
  145.     char lastWasPeriod;
  146.     char state;
  147.  
  148.     for(lastWasPeriod=TRUE, c=text;   *c;  c++) {
  149.     if (
  150.         *c != '.'
  151.         &&
  152.         *c != '?'
  153.         &&
  154.         *c != '!'
  155.     ) {
  156.         if (isAlpha(*c)) {
  157.         if (lastWasPeriod)    *c    = toUpper(*c);
  158.         else            *c    = toLower(*c);
  159.         lastWasPeriod    = FALSE;
  160.         }
  161.     } else {
  162.         lastWasPeriod    = TRUE ;
  163.     }
  164.     }
  165.  
  166.     /* little state machine to search for ' i ': */
  167. #define NUTHIN        0
  168. #define FIRSTBLANK    1
  169. #define BLANKI        2
  170.     for (state=NUTHIN, c=text;    *c;  c++) {
  171.     switch (state) {
  172.     case NUTHIN:
  173.         if (isSpace(*c))    state    = FIRSTBLANK;
  174.         else        state    = NUTHIN    ;
  175.         break;
  176.     case FIRSTBLANK:
  177.         if (*c == 'i')    state    = BLANKI    ;
  178.         else        state    = NUTHIN    ;
  179.         break;
  180.     case BLANKI:
  181.         if (isSpace(*c))    state    = FIRSTBLANK;
  182.         else        state    = NUTHIN    ;
  183.  
  184.         if (!isAlpha(*c))    *(c-1)    = 'I';
  185.         break;
  186.     }
  187.     }
  188. }
  189.  
  190. /************************************************************************/
  191. /*    flushMsgBuf() wraps up writing a message to disk        */
  192. /************************************************************************/
  193. flushMsgBuf() {
  194.     seek(msgfl, thisSector, 0);
  195.     crypte(sectBuf, SECTSIZE, 0);
  196.     if (write(msgfl, sectBuf, 1) != 1) {
  197.     printf("?ctdlmsg.sys write fail");
  198.     }
  199.     crypte(sectBuf, SECTSIZE, 0);
  200. }
  201.  
  202. /************************************************************************/
  203. /*    getMessage() reads a message off disk into RAM.         */
  204. /*    a previous call to setUp has specified the message.        */
  205. /************************************************************************/
  206. getMessage() {
  207.     char c;
  208.  
  209.     /* clear msgBuf out */
  210.     msgBuf.mbauth[ 0]    = '\0';
  211.     msgBuf.mbdate[ 0]    = '\0';
  212.     msgBuf.mborig[ 0]    = '\0';
  213.     msgBuf.mboname[0]    = '\0';
  214.     msgBuf.mbroom[ 0]    = '\0';
  215.     msgBuf.mbsrcId[0]    = '\0';
  216.     msgBuf.mbtext[ 0]    = '\0';
  217.     msgBuf.mbto[   0]    = '\0';
  218.  
  219.     do c = getMsgChar(); while (c != 0xFF);    /* find start of msg    */
  220.  
  221.     msgBuf.mbheadChar    = oldChar;        /* record location    */
  222.     msgBuf.mbheadSector = oldSector;
  223.  
  224.     getMsgStr(msgBuf.mbId, NAMESIZE);
  225.  
  226.     do    {
  227.     c = getMsgChar();
  228.     switch (c) {
  229.     case 'A':    getMsgStr(msgBuf.mbauth,  NAMESIZE);    break;
  230.     case 'D':    getMsgStr(msgBuf.mbdate,  NAMESIZE);    break;
  231.     case 'M':    /* just exit -- we'll read off disk */    break;
  232.     case 'N':    getMsgStr(msgBuf.mboname, NAMESIZE);    break;
  233.     case 'O':    getMsgStr(msgBuf.mborig,  NAMESIZE);    break;
  234.     case 'R':    getMsgStr(msgBuf.mbroom,  NAMESIZE);    break;
  235.     case 'S':    getMsgStr(msgBuf.mbsrcId, NAMESIZE);    break;
  236.     case 'T':    getMsgStr(msgBuf.mbto,      NAMESIZE);    break;
  237.     default:
  238.         getMsgStr(msgBuf.mbtext, MAXTEXT);    /* discard unknown field  */
  239.         msgBuf.mbtext[0]    = '\0';
  240.         break;
  241.     }
  242.     } while (c != 'M'  &&  isAlpha(c));
  243. }
  244.  
  245. /************************************************************************/
  246. /*    getMsgChar() returns sequential chars from message on disk    */
  247. /************************************************************************/
  248. char getMsgChar()
  249. {
  250.     char visible();
  251.     char toReturn;
  252.  
  253.     if (GMCCache) {    /* someone did an unGetMsgChar() --return it    */
  254.     toReturn= GMCCache;
  255.     GMCCache= '\0';
  256.     return toReturn;
  257.     }
  258.  
  259.     oldChar    = thisChar;
  260.     oldSector    = thisSector;
  261.  
  262.     toReturn    = sectBuf[thisChar];
  263.  
  264. #ifdef XYZZY
  265.     if (debug) putCh(visible(toReturn));
  266. #endif
  267.  
  268.     thisChar    = ++thisChar % SECTSIZE;
  269.     if (thisChar == 0) {
  270.     /* time to read next sector in: */
  271.     thisSector  = ++thisSector % maxMSector;
  272.     seek(msgfl, thisSector, 0);
  273.     if (read(msgfl, sectBuf, 1) >= 1000) {
  274.         printf("?nextMsgChar-read fail");
  275.     }
  276.     crypte(sectBuf, SECTSIZE, 0);
  277.     }
  278.     return(toReturn);
  279. }
  280.  
  281. /************************************************************************/
  282. /*    getMsgStr() reads a string from message.buf            */
  283. /************************************************************************/
  284. getMsgStr(dest, lim)
  285. char *dest;
  286. int  lim;
  287. {
  288.     char c;
  289.  
  290.     while (c = getMsgChar()) {        /* read the complete string    */
  291.     if (lim) {            /* if we have room then     */
  292.         lim--;
  293.         *dest++ = c;        /* copy char to buffer        */
  294.     }
  295.     }
  296.     *dest = '\0';            /* tie string off with null    */
  297. }
  298.  
  299. /************************************************************************/
  300. /*    getWord() fetches one word from current message         */
  301. /************************************************************************/
  302. int getWord(dest, source, offset, lim)
  303. char *dest, *source;
  304. int  lim, offset;
  305. {
  306.     int i, j;
  307.  
  308.     /* skip leading blanks if any */
  309.     for (i=0;  source[offset+i]==' ' && i<lim;    i++);
  310.  
  311.     /* step over word */
  312.     for (;
  313.  
  314.      source[offset+i]   != ' '    &&
  315.      i            <  lim    &&
  316.      source[offset+i]   != 0;
  317.  
  318.      i++
  319.     );
  320.  
  321.     /* pick up any trailing blanks */
  322.     for (;  source[offset+i]==' ' && i<lim;  i++);
  323.  
  324.     /* copy word over */
  325.     for (j=0;  j<i;  j++)  dest[j] = source[offset+j];
  326.     dest[j] = 0;    /* null to tie off string */
  327.  
  328.     return(offset+i);
  329. }
  330.  
  331. /************************************************************************/
  332. /*    mAbort() returns TRUE if the user has aborted typeout        */
  333. /*    Globals modified:    outFlag                 */
  334. /************************************************************************/
  335. char mAbort()
  336. {
  337.     char BBSCharReady(), iChar(), toUpper();
  338.     char c, toReturn, oldEcho;
  339.  
  340.     /* Check for abort/pause from user */
  341.     if (!BBSCharReady()) {
  342.     toReturn    = FALSE;
  343.     } else {
  344.     oldEcho = echo;
  345.     echo    = NEITHER;
  346.     c = toUpper(iChar());
  347.     switch (c) {
  348.     case XOFF:
  349.     case 'P':                    /*    pause:          */
  350.         c = iChar();                /* wait to resume */
  351.         if (
  352.         toLower(c) == 'd'
  353.         &&
  354.         aide
  355.         ) pullMessage = TRUE;
  356.         toReturn      = FALSE;
  357.         break;
  358.     case 'J':                    /* jump paragraph:*/
  359.         outFlag    = OUTPARAGRAPH;
  360.         toReturn    = FALSE;
  361.         break;
  362.     case 'N':                    /* next:          */
  363.         outFlag    = OUTNEXT;
  364.         toReturn    = TRUE;
  365.         break;
  366.     case 'S':                    /* skip:          */
  367.         outFlag    = OUTSKIP;
  368.         toReturn    = TRUE;
  369.         break;
  370.     default:
  371.         toReturn    = FALSE;
  372.         break;
  373.     }
  374.     echo    = oldEcho;
  375.     }
  376.     return toReturn;
  377. }
  378.  
  379. /************************************************************************/
  380. /*    makeMessage is menu-level routine to enter a message        */
  381. /*    Return: TRUE if message saved else FALSE            */
  382. /************************************************************************/
  383. makeMessage(uploading, name_Auth)
  384. char *name_Auth;    /* name of recipient unless name_Auth == 0 */
  385. char uploading;     /* TRUE if message is coming via WC protocol    */
  386. {
  387.     char        putMessage();
  388.     char        allUpper, *pc, toReturn;
  389.     struct logBuffer    lBuf;
  390.     int         logNo;
  391.  
  392.     toReturn = FALSE;
  393.  
  394.     logNo    = ERROR;/* not needed, but it's nice to initialize...    */
  395.  
  396.     if (thisRoom != MAILROOM && !loggedIn && !unlogEnterOk) {
  397.     mPrintf("Must log in to enter messages except MAIL to the SYSOP\n ");
  398.     return FALSE;
  399.     }
  400.  
  401.     if (thisRoom != MAILROOM)  msgBuf.mbto[0] = FALSE;
  402.     else {
  403.     if (!loggedIn || (!aide && noMail)) {
  404.         strCpy(msgBuf.mbto, "Sysop");
  405.         mPrintf(" (private mail to 'sysop')\n ");
  406.     } else {
  407.         if (name_Auth == 0)
  408.         getNormStr("recipient", msgBuf.mbto, NAMESIZE);
  409.         else
  410.         strcpy(msgBuf.mbto, name_Auth);
  411.         logNo   = findPerson(msgBuf.mbto, &lBuf);
  412.         if ((logNo==ERROR  &&  hash(msgBuf.mbto) != hash("Sysop")) ||
  413.                     strlen(msgBuf.mbto) == 0) {
  414.         mPrintf("No '%s' known", msgBuf.mbto);
  415.         return FALSE;
  416.         }
  417.         if (hash(msgBuf.mbto) != hash("Sysop"))
  418.         strCpy(msgBuf.mbto, lBuf.lbname);
  419.     }
  420.     }
  421.  
  422.     strcpy(msgBuf.mbauth, logBuf.lbname);        /* record author*/
  423.     if (uploading ||
  424.     getText("message", msgBuf.mbtext, MAXTEXT, msgBuf.mbto)) {
  425.     if (!uploading)   {
  426.         for (pc=msgBuf.mbtext, allUpper=TRUE;   *pc && allUpper;  pc++)   {
  427.         if (toUpper(*pc) != *pc)   allUpper = FALSE;
  428.         }
  429.         if (allUpper)   fakeFullCase(msgBuf.mbtext, MAXTEXT);
  430.     }
  431.  
  432.     if (toReturn=putMessage(uploading))   noteMessage(&lBuf, logNo);
  433.     }
  434.     return toReturn;
  435. }
  436.  
  437. /************************************************************************/
  438. /*    mFormat() formats a string to modem and console         */
  439. /************************************************************************/
  440. mFormat(string)
  441. char *string;
  442. #define MAXWORD 256    /* maximum length of a word */
  443. {
  444.     char wordBuf[MAXWORD];
  445.     char mAbort();
  446.     int  i;
  447.  
  448.     for (i=0;  string[i] && (!outFlag || outFlag==OUTPARAGRAPH);  ) {
  449.     i = getWord(wordBuf, string, i, MAXWORD);
  450.     putWord(wordBuf);
  451.     if (mAbort()) return;
  452.     }
  453. }
  454.  
  455. /************************************************************************/
  456. /*    mPeek() dumps a sector in message.buf.    sysop debugging tool    */
  457. /************************************************************************/
  458. mPeek() {
  459.     char visible();
  460.     char peekBuf[SECTSIZE];
  461.     int  col, row, s;
  462.  
  463.     s = getNumber(" sector to dump", 0, maxMSector-1);
  464.     seek(msgfl, s, 0);
  465.     read(msgfl, peekBuf, 1);
  466.     for (row=0;  row<2;  row++) {
  467.     mPrintf("\n ");
  468.     for (col=0;  col<64;  col++) {
  469.         oChar(visible(peekBuf[row*64 +col]));
  470.     }
  471.     }
  472. }
  473.  
  474. /************************************************************************/
  475. /*    mPrintf() formats format+args to modem and console        */
  476. /************************************************************************/
  477. mPrintf(format /* plus an unknown #arguments for format */)
  478. char *format;
  479. #define MAXWORD 256    /* maximum length of a word */
  480. {
  481.     int  _sspr();
  482.     char mAbort();
  483.     char string[MAXWORD], wordBuf[MAXWORD], *s;
  484.     int  i;
  485.  
  486.     s = string;
  487.     _spr(&format, &_sspr, &s);    /* _sspr is a BDS internal function for */
  488.                 /* sprintf. */
  489.     *s = '\0';
  490.     for (i=0;  string[i] && (!outFlag  || outFlag==OUTPARAGRAPH);  ) {
  491.     i = getWord(wordBuf, string, i, MAXWORD);
  492.     putWord(wordBuf);
  493.  
  494.     if (mAbort()) return;
  495.     }
  496. }
  497.  
  498. /************************************************************************/
  499. /*    mWCprintf() formats format+args to sendWCChar()         */
  500. /************************************************************************/
  501. mWCprintf(format /* plus an unknown #arguments for format */)
  502. char *format;
  503. {
  504.     int  sendWCChar();
  505.  
  506.     _spr(&format, &sendWCChar);
  507.     sendWCChar(0);        /* Send NULL since it did before */
  508. }
  509.  
  510. /************************************************************************/
  511. /*    msgInit() sets up lowId, highId, catSector and catChar,     */
  512. /*    by scanning over message.buf                    */
  513. /************************************************************************/
  514. msgInit() {
  515.     int  aToI();
  516.     int  firstLo, firstHi, hereLo, hereHi;    /* 32 bits by halves    */
  517.  
  518.     startAt(0, 0);
  519.     getMessage();
  520.  
  521.     /* get the ID# */
  522.     sscanf(msgBuf.mbId, "%d %d", &firstHi, &firstLo);
  523.     printf("message# %d %d\n", firstHi, firstLo);
  524.     newestHi    = firstHi;
  525.     newestLo    = firstLo;
  526.  
  527.     oldestHi    = firstHi;
  528.     oldestLo    = firstLo;
  529.  
  530.     catSector    = thisSector;
  531.     catChar    = thisChar;
  532.  
  533.     for (
  534.     getMessage();
  535.  
  536.     sscanf(msgBuf.mbId, "%d %d", &hereHi, &hereLo),
  537.     !(hereHi == firstHi   &&   hereLo == firstLo);
  538.  
  539.     getMessage()
  540.  
  541.     ) {
  542.     printf("message# %d %d\n", hereHi, hereLo);
  543.  
  544.     /* find highest and lowest message IDs: */
  545.     /* 32-bit "<" by hand: */
  546.     if ((hereHi<oldestHi)  ||  (hereHi==oldestHi && hereLo<oldestLo)) {
  547.         oldestHi    = hereHi;
  548.         oldestLo    = hereLo;
  549.         printf(" oldest=%u %u\n", oldestHi, oldestLo);
  550.     }
  551.     if ((hereHi>newestHi)  ||  (hereHi==newestHi && hereLo>newestLo)) {
  552.         newestHi    = hereHi;
  553.         newestLo    = hereLo;
  554.         printf(" newest=%u %u\n", newestHi, newestLo);
  555.  
  556.         /* read rest of message in and remember where it ends,    */
  557.         /* in case it turns out to be the last message        */
  558.         /* in which case, that's where to start writing next message*/
  559.         while (dGetWord(msgBuf.mbtext, MAXTEXT));
  560.         catSector    = thisSector;
  561.         catChar    = thisChar;
  562.     }
  563.     }
  564. }
  565.  
  566. /************************************************************************/
  567. /*    noteLogMessage() slots message into log record            */
  568. /************************************************************************/
  569. noteLogMessage(lBuf, logNo)
  570. struct logBuffer   *lBuf;
  571. int           logNo;
  572. {
  573.     int i;
  574.  
  575.     /* store into recipient's log record: */
  576.     /* slide message pointers down to make room for this one: */
  577.     for (i=0;  i<MAILSLOTS-1;  i++) {
  578.     (*lBuf).lbslot[i]   = (*lBuf).lbslot[i+1];
  579.     (*lBuf).lbId[  i]   = (*lBuf).lbId[  i+1];
  580.     }
  581.  
  582.     /* slot this message in:    */
  583.     (*lBuf).lbId[MAILSLOTS-1]        = newestLo ;
  584.     (*lBuf).lbslot[MAILSLOTS-1]     = catSector;
  585.  
  586.     putLog(lBuf, logNo);
  587. }
  588.  
  589. /************************************************************************/
  590. /*    noteMessage() slots message into current room            */
  591. /************************************************************************/
  592. noteMessage(lBuf, logNo)
  593. struct logBuffer   *lBuf;
  594. int           logNo;
  595. {
  596.     if (!++newestLo) ++newestHi;    /* 32-bit '++' by hand    */
  597.     logBuf.lbvisit[0]    = newestLo;
  598.  
  599.     if (thisRoom != MAILROOM) {
  600.     note2Message(newestLo, catSector);
  601.  
  602.     /* write it to disk:        */
  603.     putRoom(thisRoom);
  604.     noteRoom();
  605.     } else {
  606.     if (hash(msgBuf.mbto) != hash("Sysop"))  {
  607.         if (logNo != thisLog)  {
  608.         noteLogMessage(lBuf, logNo);        /* note in recipient    */
  609.         }
  610.         noteLogMessage(&logBuf, thisLog);        /* note in ourself        */
  611.         fillMailRoom();                /* update room also     */
  612.     } else {
  613.        getRoom(AIDEROOM);
  614.  
  615.        /* enter in Aide> room -- 'sysop' is special */
  616.        note2Message(newestLo, catSector);
  617.  
  618.        /* write it to disk:        */
  619.        putRoom(AIDEROOM);
  620.        noteRoom();
  621.  
  622.        getRoom(MAILROOM);
  623.        /* note in ourself if logged in: */
  624.        if (loggedIn)   noteLogMessage(&logBuf, thisLog);
  625.        fillMailRoom();
  626.     }
  627.     }
  628.  
  629.     /* make message official:    */
  630.     catSector    = thisSector;
  631.     catChar    = thisChar;
  632.     setUp(FALSE);
  633. }
  634.  
  635. /************************************************************************/
  636. /*    note2Message() makes slot in current room... called by noteMess */
  637. /************************************************************************/
  638. note2Message(id, loc)
  639. int id, loc;
  640. {
  641.     int i;
  642.  
  643.     /* store into current room: */
  644.     /* slide message pointers down to make room for this one:        */
  645.     for (i=0;  i<MSGSPERRM-1;  i++) {
  646.     roomBuf.msg[i].rbmsgLoc  = roomBuf.msg[i+1].rbmsgLoc;
  647.     roomBuf.msg[i].rbmsgNo     = roomBuf.msg[i+1].rbmsgNo ;
  648.     }
  649.  
  650.     /* slot this message in:        */
  651.     roomBuf.msg[MSGSPERRM-1].rbmsgNo     = id ;
  652.     roomBuf.msg[MSGSPERRM-1].rbmsgLoc     = loc;
  653. }
  654.  
  655.  
  656. /************************************************************************/
  657. /*    printMessage() prints indicated message on modem & console    */
  658. /************************************************************************/
  659. printMessage(loc, id, name_Auth)
  660. char     *name_Auth; /* Author buffer if thisRoom == mailRoom */
  661. int     loc;         /* sector in message.buf         */
  662. unsigned id;         /* unique-for-some-time ID#     */
  663. {
  664.     char dGetWord(), mAbort();
  665.     char c, moreFollows;
  666.     int hereHi, hereLo;
  667.  
  668.     startAt(loc, 0);
  669.  
  670.     do getMessage(); while (
  671.     (
  672.         sscanf(msgBuf.mbId, "%d %d", &hereHi, &hereLo),
  673.         hereLo != id
  674.     ) &&
  675.     thisSector == loc
  676.     );
  677.     if (hereLo != id  &&  !usingWCprotocol) {
  678.     mPrintf("?can't find message");
  679. #ifdef XYZZY
  680.     mPrintf(" loc=%d, id=%u, mbIds=%s, here=%d %d\n",
  681.         loc, id, msgBuf.mbId, &hereHi, &hereLo
  682.     );
  683. #endif
  684.     return;
  685.     }
  686.  
  687.     if (!usingWCprotocol) {
  688.     doCR();
  689.  
  690.     if (msgBuf.mbdate[ 0])        mPrintf(    "   %s ",     msgBuf.mbdate );
  691.     if (msgBuf.mbauth[ 0]) {
  692.         mPrintf(    "from %s",    msgBuf.mbauth );
  693.         if (thisRoom == MAILROOM)
  694.         strcpy(name_Auth, msgBuf.mbauth);
  695.     }
  696.     if (msgBuf.mboname[0])        mPrintf(    " @%s",       msgBuf.mboname);
  697.     if (
  698.         msgBuf.mbroom[0]
  699.         &&
  700.         strCmp(msgBuf.mbroom, roomBuf.rbname) != SAMESTRING
  701.     ) {
  702.         mPrintf(                " in %s>",    msgBuf.mbroom );
  703.     }
  704.     if (msgBuf.mbto[   0])        mPrintf(    " to %s",     msgBuf.mbto   );
  705.  
  706.     doCR();
  707.  
  708.     while (1) {
  709.         moreFollows     = dGetWord(msgBuf.mbtext, 150);
  710.         putWord(msgBuf.mbtext);
  711.         if (!(moreFollows  &&  !mAbort())) {
  712.         if (outFlag == OUTNEXT)     /* If <N>ext, extra line */
  713.             doCR();
  714.         break;
  715.         }
  716.     }
  717.     doCR();
  718.  
  719.     } else {
  720.     /* networking dump of message: */
  721.  
  722.     /* fill in local node in origin fields if local message: */
  723.     if (!msgBuf.mborig[ 0])  strcpy(msgBuf.mborig,    nodeId       );
  724.     if (!msgBuf.mboname[0])  strcpy(msgBuf.mboname, nodeName   );
  725.     if (!msgBuf.mbsrcId[0])  strcpy(msgBuf.mbsrcId, msgBuf.mbId);
  726.  
  727.     /* send header fields out: */
  728.     if (msgBuf.mbauth[ 0])    mWCprintf("A%s", msgBuf.mbauth );
  729.     if (msgBuf.mbdate[ 0])    mWCprintf("D%s", msgBuf.mbdate );
  730.     if (msgBuf.mboname[0])    mWCprintf("N%s", msgBuf.mboname);
  731.     if (msgBuf.mborig[ 0])    mWCprintf("O%s", msgBuf.mborig );
  732.     if (msgBuf.mbroom[ 0])    mWCprintf("R%s", msgBuf.mbroom );
  733.     if (msgBuf.mbsrcId[0])    mWCprintf("S%s", msgBuf.mbsrcId);
  734.     if (msgBuf.mbto[   0])    mWCprintf("T%s", msgBuf.mbto   );
  735.  
  736.     /* send message text proper: */
  737.     sendWCChar('M');
  738.     do  {
  739.         c = getMsgChar();
  740.         if (c=='\n')  c='\r';
  741.         if (!sendWCChar(c)) break;
  742.     } while (c);
  743.     }
  744. }
  745.  
  746. /************************************************************************/
  747. /*    pullIt() is a aide special to remove a message from a room    */
  748. /************************************************************************/
  749. pullIt(m)
  750. int m;
  751. {
  752.     int  i;
  753.     char blah[NAMESIZE];
  754.  
  755.     /* confirm that we're removing the right one:    */
  756.     outFlag = OUTOK;
  757.     printMessage(roomBuf.msg[m].rbmsgLoc, roomBuf.msg[m].rbmsgNo, blah);
  758.     if (!getYesNo("pull")) return FALSE;
  759.  
  760.     /* record vital statistics for possible insertion elsewhere: */
  761.     pulledMLoc = roomBuf.msg[m].rbmsgLoc;
  762.     pulledMId  = roomBuf.msg[m].rbmsgNo ;
  763.  
  764.     if (thisRoom == AIDEROOM)    return TRUE;
  765.  
  766.     /* return emptied slot: */
  767.     for (i=m;  i>0;  i--) {
  768.     roomBuf.msg[i].rbmsgLoc      = roomBuf.msg[i-1].rbmsgLoc;
  769.     roomBuf.msg[i].rbmsgNo         = roomBuf.msg[i-1].rbmsgNo ;
  770.     }
  771.     roomBuf.msg[0].rbmsgNo   = 0;    /* mark new slot at end as free */
  772.     roomBuf.msg[0].rbmsgLoc  = ERROR;    /* mark new slot at end as free */
  773.  
  774.     /* store revised room to disk before we forget...    */
  775.     noteRoom();
  776.     putRoom(thisRoom);
  777.  
  778.     /* note in Aide>: */
  779.     sPrintf(msgBuf.mbtext, "Following message deleted by %s:", logBuf.lbname);
  780.     aideMessage( /* noteDeletedMessage == */ TRUE);
  781.     return TRUE;
  782. }
  783.  
  784. /************************************************************************/
  785. /************************************************************************/
  786. char putMessage(uploading)
  787. char uploading;
  788. {
  789.     char *s, allOk;
  790.     int  putWCChar();
  791.  
  792.     startAt(catSector, catChar);    /* tell putMsgChar where to write    */
  793.     putMsgChar(0xFF);            /* start-of-message         */
  794.  
  795.     /* write message ID */
  796.     dPrintf("%u %u", newestHi, newestLo+1);
  797.     putMsgChar(0);
  798.  
  799.     /* write date:    */
  800.     dPrintf("D%d%s%02d",
  801.     interpret(pGetYear),
  802.     monthTab[interpret(pGetMonth)],
  803.     interpret(pGetDay)
  804.     );
  805.     putMsgChar(0);
  806.  
  807.     /* write room name out:        */
  808.     dPrintf("R%s", roomBuf.rbname);
  809.     putMsgChar(0);
  810.  
  811.     if (loggedIn) {
  812.     /* write author's name out:        */
  813.     dPrintf("A%s", msgBuf.mbauth);
  814.     putMsgChar(0);        /* null to end string   */
  815.     }
  816.  
  817.     if (msgBuf.mbto[0]) {    /* private message -- write addressee    */
  818.     dPrintf("T%s", msgBuf.mbto);
  819.     putMsgChar(0);
  820.     }
  821.  
  822.     /* write message text by hand because it would overrun dPrintf buffer: */
  823.     putMsgChar('M');    /* M-for-message.    */
  824.     if (!uploading) {
  825.     for (s=msgBuf.mbtext;  *s;  s++) putMsgChar(*s);
  826.     allOk    = TRUE;
  827.     } else {
  828.     outFlag = FALSE;    /* setup for putWCChar()    */
  829.     allOk    = readFile(putWCChar);
  830.     }
  831.  
  832.     if (allOk) {
  833.     putMsgChar(0);        /* null to end text     */
  834.  
  835.     flushMsgBuf();
  836.     } else {
  837.     flushMsgBuf();        /* so message count is ok    */
  838.  
  839.     /* erase start-of-message indicator: */
  840.     startAt(catSector, catChar);
  841.     putmsgChar(0);        /* overwrite 0xFF byte    */
  842.     }
  843.  
  844.     return  allOk;
  845. }
  846.  
  847. /************************************************************************/
  848. /*    putMsgChar() writes successive message chars to disk        */
  849. /*    Globals:    thisChar=    thisSector=            */
  850. /*    Returns:    ERROR if problems else TRUE            */
  851. /************************************************************************/
  852. int putMsgChar(c)
  853. char c;
  854. {
  855.     char visible();
  856.     int  toReturn;
  857.  
  858.     toReturn = TRUE;
  859.  
  860. #ifdef XYZZY
  861.     if (debug) putch(visible(c));
  862. #endif
  863.  
  864.     if (sectBuf[thisChar] == 0xFF)  {
  865.     /* obliterating a msg    */
  866.     if (!++oldestLo) ++oldestHi;    /* 32-bit increment by hand    */
  867.     logBuf.lbvisit[(MAXVISIT-1)]    = oldestLo;
  868.     }
  869.  
  870.     sectBuf[thisChar]    = c;
  871.  
  872.     thisChar    = ++thisChar % SECTSIZE;
  873.  
  874.     if (thisChar == 0) {    /* time to write sector out a get next: */
  875.     seek(msgfl, thisSector, 0);
  876.     crypte(sectBuf, SECTSIZE, 0);
  877.     if (write(msgfl, sectBuf, 1) != 1) {
  878.         printf("?putMsgChar-write fail");
  879.         toReturn    = ERROR;
  880.     }
  881.  
  882.     thisSector    = ++thisSector % maxMSector;
  883.     seek(msgfl, thisSector, 0);
  884.     if (read(msgfl, sectBuf, 1) >= 1000) {
  885.         printf("?putMsgChar-read fail");
  886.         toReturn    = ERROR;
  887.     }
  888.     crypte(sectBuf, SECTSIZE, 0);
  889.     }
  890.     return  toReturn;
  891. }
  892.  
  893. /************************************************************************/
  894. /*    putWord() writes one word to modem & console            */
  895. /************************************************************************/
  896. putWord(st)
  897. char *st;
  898. {
  899.     char *s;
  900.     int  newColumn;
  901.  
  902.     for (newColumn=crtColumn, s=st;  *s; s++)    {
  903.     if (*s != TAB)    ++newColumn;
  904.     else        while (++newColumn % 8);
  905.     }
  906.     if (newColumn > termWidth)     doCR();
  907.  
  908.     for (;  *st;  st++) {
  909.  
  910.     if (*st != TAB) ++crtColumn;
  911.     else        while (++crtColumn % 8);
  912.  
  913.     /* worry about words longer than a line:    */
  914.     if (crtColumn > termWidth) doCR();
  915.  
  916.     if (prevChar!=NEWLINE  ||  (*st > ' '))   oChar(*st);
  917.     else {
  918.         /* end of paragraph: */
  919.         if (outFlag == OUTPARAGRAPH)   {
  920.         outFlag = OUTOK;
  921.         }
  922.         doCR();
  923.         oChar(*st);
  924.     }
  925.     }
  926. }
  927.  
  928. /************************************************************************/
  929. /*    showMessages() is routine to print roomful of msgs        */
  930. /************************************************************************/
  931. showMessages(whichMess, revOrder)
  932. char whichMess, revOrder;
  933. {
  934.     char name_Auth[NAMESIZE], pulled;
  935.     char toUpper(), iChar();
  936.     int i;
  937.     int start, finish, increment, msgNo;
  938.     unsigned lowLim, highLim;
  939.  
  940.     setUp(FALSE);
  941.     if (whichIO != CONSOLE && thisRoom == MAILROOM) echo = CALLER;
  942.  
  943.     /* Allow for reverse retrieval: */
  944.     if (!revOrder) {
  945.     start        = 0;
  946.     finish        = MSGSPERRM;
  947.     increment   = 1;
  948.     } else {
  949.     start        = (MSGSPERRM -1);
  950.     finish        = -1;
  951.     increment   = -1;
  952.     }
  953.  
  954.     switch (whichMess)     {
  955.     case NEWoNLY:
  956.     lowLim    = logBuf.lbvisit[ logBuf.lbgen[thisRoom] & CALLMASK]+1;
  957.     highLim = newestLo;
  958.     break;
  959.     case OLDaNDnEW:
  960.     lowLim    = oldestLo;
  961.     highLim = newestLo;
  962.     break;
  963.     case OLDoNLY:
  964.     lowLim    = oldestLo;
  965.     highLim = logBuf.lbvisit[ logBuf.lbgen[thisRoom] & CALLMASK];
  966.     break;
  967.     }
  968.  
  969.     /* stuff may have scrolled off system unseen, so: */
  970.     /* was "if (lowLim < oldestLo)...", rigged for wraparound: */
  971.     if (oldestLo-lowLim < 0x8000) {
  972.     lowLim = oldestLo;
  973.     }
  974.     if (!expert && !usingWCprotocol)  {
  975.     mPrintf("\n <J>ump <N>ext <P>ause <S>top");
  976.     }
  977.  
  978.     for (i=start;   i!=finish;     i+=increment) {
  979.     if (outFlag) {
  980.         if        (
  981.              outFlag == OUTNEXT
  982.              ||
  983.              outFlag == OUTPARAGRAPH
  984.         ) outFlag = OUTOK;
  985.         else if (outFlag == OUTSKIP)   {
  986.         echo = BOTH;
  987.         return;
  988.         }
  989.     }
  990.  
  991.     /* "<" comparison with 64K wraparound in mind: */
  992.     msgNo    = roomBuf.msg[i].rbmsgNo;
  993.     if (
  994.         msgNo - lowLim  < 0x8000
  995.         &&
  996.         highLim - msgNo < 0x8000
  997.      ) {
  998.         printMessage(roomBuf.msg[i].rbmsgLoc, msgNo, name_Auth);
  999.         if (usingWCprotocol && WCError)
  1000.         return;
  1001.  
  1002.         /*    Pull current message from room if flag set */
  1003.         if (pullMessage) {
  1004.         pullMessage = FALSE;
  1005.         pulled = pullIt(i);
  1006.         if (revOrder)    i++;
  1007.         }
  1008.         else
  1009.         pulled = FALSE;
  1010.  
  1011.         if (
  1012.         !usingWCprotocol
  1013.         &&
  1014.         !pulled
  1015.         &&
  1016.         strCmpU(name_Auth, logBuf.lbname) != SAMESTRING
  1017.         &&
  1018.         thisRoom  == MAILROOM
  1019.         &&
  1020.         whichMess == NEWoNLY
  1021.         &&
  1022.         getYesNo("respond")
  1023.         ) {
  1024.         if (makeMessage( /* uploading== */ FALSE, name_Auth)) i--;
  1025.         if (whichIO != CONSOLE && thisRoom == MAILROOM)
  1026.             echo = CALLER;    /* Restore privacy zapped by make... */
  1027.         }
  1028.     }
  1029.     }
  1030.     echo = BOTH;
  1031. }
  1032.  
  1033. /************************************************************************/
  1034. /*    startAt() sets location to begin reading message from        */
  1035. /************************************************************************/
  1036. startAt(sect, byt)
  1037. int sect;
  1038. int byt;
  1039. {
  1040.     GMCCache  = '\0';    /* cache to unGetMsgChar() into */
  1041.  
  1042.     if (sect >= maxMSector) {
  1043.     printf("?startAt s=%d,b=%d", sect, byt);
  1044.     return;
  1045.     }
  1046.     thisChar    = byt;
  1047.     thisSector    = sect;
  1048.  
  1049.     seek(msgfl, sect, 0);
  1050.     if (read(msgfl, sectBuf, 1) >= 1000) {
  1051.     printf("?startAt read fail");
  1052.     }
  1053.     crypte(sectBuf, SECTSIZE, 0);
  1054. }
  1055.  
  1056. /************************************************************************/
  1057. /*    unGetMsgChar() returns (at most one) char to getMsgChar()    */
  1058. /************************************************************************/
  1059. unGetMsgChar(c)
  1060. char c;
  1061. {
  1062.     GMCCache    = c;
  1063. }
  1064.  
  1065. /************************************************************************/
  1066. /*    zapMsgFl() initializes message.buf                */
  1067. /************************************************************************/
  1068. zapMsgFile() {
  1069.     char getCh(), toUpper();
  1070.     int i, sect, val;
  1071.  
  1072.     printf("\nDestroy all current messages? ");
  1073.     if (toUpper(getCh()) != 'Y')   return;
  1074.  
  1075.     /* put null message in first sector... */
  1076.     sectBuf[0]    = 0xFF; /*   \                */
  1077.     sectBuf[1]    =  '0'; /*    \             */
  1078.     sectBuf[2]    =  ' '; /*     > Message ID "0 1"    */
  1079.     sectBuf[3]    =  '1'; /*    /             */
  1080.     sectBuf[4]    = '\0'; /*   /                */
  1081.     sectBuf[5]    =  'M'; /*   \    Null messsage        */
  1082.     sectBuf[6]    = '\0'; /*   /                */
  1083.  
  1084.     for (i=7;  i<SECTSIZE;  i++) sectBuf[i] = 0;
  1085.  
  1086.     seek(msgfl, 0, 0);
  1087.     crypte(sectBuf, SECTSIZE, 0);    /* encrypt    */
  1088.     if ((val = write(msgfl, sectBuf, 1)) != 1) {
  1089.     printf("zapMsgFil: write failed, %d records!\n", val);
  1090.     }
  1091.  
  1092.     crypte(sectBuf, SECTSIZE, 0);    /* decrypt    */
  1093.     sectBuf[0] = 0;
  1094.     crypte(sectBuf, SECTSIZE, 0);    /* encrypt    */
  1095.     for (sect=1;  sect<maxMSector;  sect++) {
  1096.     seek(msgfl, sect, 0);
  1097.     if ((val = write(msgfl, sectBuf, 1)) != 1) {
  1098.         printf("zapMsgFil: write failed, wrote %d records!\n", val);
  1099.     }
  1100.     }
  1101.     crypte(sectBuf, SECTSIZE, 0);    /* decrypt    */
  1102. }
  1103. roy all current messages? ");
  1104.     if (toUpper(getCh()) != 'Y')   re